home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 March / macformat-022.iso / Shareware City / Developers / src / out-of-phase-102-c / OutOfPhase 1.02 Source / OutOfPhase Folder / Level 0 Macintosh 29Sep94 / DirStuff.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-23  |  24.6 KB  |  880 lines  |  [TEXT/KAHL]

  1. /* DirStuff.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Debug.h"
  21. #include "Audit.h"
  22. #include "Definitions.h"
  23.  
  24. #ifdef THINK_C
  25.     #pragma options(pack_enums)
  26. #endif
  27. #include <Files.h>
  28. #include <Errors.h>
  29. #include <Aliases.h>
  30. #include <Script.h>
  31. #ifdef THINK_C
  32.     #pragma options(!pack_enums)
  33. #endif
  34.  
  35. #include "DirStuff.h"
  36. #include "Memory.h"
  37. #include "Files.h"
  38.  
  39.  
  40. #define MaxFilenameSize (256)
  41.  
  42. typedef struct DirEntry
  43.     {
  44.         struct DirEntry*        Next;
  45.         FileSpec*                        WhereIsIt;
  46.         DirFileTypes                ItemType;
  47.     } DirEntry;
  48.  
  49. struct DirectoryRec
  50.     {
  51.         DirEntry*                        List;
  52.     };
  53.  
  54.  
  55. #define AUDITFILESPEC(x) APRINT((" "#x": v=%s,par=%l,%p",((FSSpec*)&(x))->vRefNum,\
  56.                     ((FSSpec*)&(x))->parID,((FSSpec*)&(x))->name))
  57.  
  58.  
  59. /* local routine to dereference a folder.  You supply the folder's location */
  60. /* in TheFolder (name + parent) and it returns the ID of the folder in *FolderID */
  61. /* (so you can use it as the parent of something inside the folder) */
  62. static OSErr            FDerefFolder(FSSpec* TheFolder, long* FolderID)
  63.     {
  64.         CInfoPBRec        MyPB;
  65.         OSErr                    Error;
  66.         FSSpec                Copy;
  67.         Boolean                TargetWasFolder;
  68.         Boolean                TargetWasAlias;
  69.  
  70.         APRINT(("+FDerefFolder v=%s,par=%l,%p",TheFolder->vRefNum,TheFolder->parID,
  71.             TheFolder->name));
  72.         Copy = *TheFolder;
  73.         AUDITFILESPEC(Copy);
  74.         Error = ResolveAliasFile(&Copy,False/*oneonly*/,&TargetWasFolder,&TargetWasAlias);
  75.         APRINT((" ResolveAliasFile:  Err = %s, Folder = %b, Alias = %b",(short)Error,
  76.             (short)TargetWasFolder,(short)TargetWasAlias));
  77.         if (Error == fnfErr)
  78.             {
  79.                 /* assume it's a volume. */
  80.                 /* return the volume's root directory number (no indirection) */
  81.                 *FolderID = Copy.parID;
  82.                 APRINT(("-FDerefFolder dirID=%l",*FolderID));
  83.                 return noErr;
  84.             }
  85.         MyPB.dirInfo.ioCompletion = NIL;
  86.         MyPB.dirInfo.ioVRefNum = Copy.vRefNum;
  87.         MyPB.dirInfo.ioNamePtr = Copy.name;
  88.         MyPB.dirInfo.ioFDirIndex = 0;
  89.         MyPB.dirInfo.ioDrDirID = Copy.parID;
  90.         Error = PBGetCatInfo(&MyPB,False);
  91.         if (Error == noErr)
  92.             {
  93.                 Error = MyPB.dirInfo.ioResult;
  94.             }
  95.         if (Error != noErr)
  96.             {
  97.             }
  98.          else
  99.             {
  100.                 if ((MyPB.dirInfo.ioFlAttrib & 16) == 0)
  101.                     {
  102.                         /* if it's not a directory, then we return error */
  103.                         Error = dirNFErr;
  104.                     }
  105.                  else
  106.                     {
  107.                         *FolderID = MyPB.dirInfo.ioDrDirID;
  108.                         APRINT(("-FDerefFolder dirID=%l",*FolderID));
  109.                     }
  110.             }
  111.         ERROR(Error != noErr,APRINT(("-FDerefFolder failed %s",Error)));
  112.         return Error;
  113.     }
  114.  
  115.  
  116. /* read the first level list of items in the specified directory.  NIL specifies */
  117. /* a root directory.  If NIL is returned, then the operation could not be completed */
  118. DirectoryRec*            ReadDirectory(FileSpec* Directory)
  119.     {
  120.         DirectoryRec*            Dir;
  121.         DirEntry*                    Tail;
  122.  
  123.         if (Directory != NIL)
  124.             {
  125.                 CheckPtrExistence(Directory);
  126.                 ValidateFileSpec(Directory);
  127.                 APRINT(("+ReadDirectory v=%s,par=%l,%p",((FSSpec*)Directory)->vRefNum,
  128.                     ((FSSpec*)Directory)->parID,((FSSpec*)Directory)->name));
  129.             }
  130.          else
  131.             {
  132.                 APRINT(("+ReadDirectory (root)"));
  133.             }
  134.         Dir = (DirectoryRec*)AllocPtrCanFail(sizeof(DirectoryRec),"DirectoryRec");
  135.         if (Dir == NIL)
  136.             {
  137.                 /* fault tolerance is most studly... */
  138.                 APRINT(("-ReadDirectory failed"));
  139.                 return NIL;
  140.             }
  141.         Tail = NIL;
  142.         Dir->List = NIL;
  143.         if ((NIL != Directory)
  144.             || ((((FSSpec*)Directory)->name[0] == 0)
  145.             && (((FSSpec*)Directory)->vRefNum == 0)
  146.             && (((FSSpec*)Directory)->parID == 0)))
  147.             {
  148.                 CInfoPBRec            CInfo;
  149.                 OSErr                        Error;
  150.                 short                        Index;
  151.                 Str255                    FileName;
  152.                 MyBoolean                EnergizerBunny;
  153.                 short                        VolumeRefNum;
  154.                 long                        DirectoryID;
  155.  
  156.                 /* read through this directory and build a list of contained items */
  157.                 VolumeRefNum = ((FSSpec*)Directory)->vRefNum;
  158.                 if (noErr != FDerefFolder((FSSpec*)Directory,&DirectoryID))
  159.                     {
  160.                         /* oops, we weren't given a directory to look at. */
  161.                         ReleasePtr((char*)Dir);
  162.                         APRINT(("-ReadDirectory failed"));
  163.                         return NIL;
  164.                     }
  165.                 Index = 0;
  166.                 EnergizerBunny = True;
  167.                 while (EnergizerBunny)
  168.                     {
  169.                         Index += 1; /* index started at 0 up there, so we increment first */
  170.                         CInfo.hFileInfo.ioVRefNum = VolumeRefNum;
  171.                         CInfo.hFileInfo.ioNamePtr = FileName;
  172.                         CInfo.hFileInfo.ioDirID = DirectoryID;
  173.                         CInfo.hFileInfo.ioFDirIndex = Index;
  174.                         Error = PBGetCatInfo(&CInfo,False/*synchronously*/);
  175.                         if (Error == noErr)
  176.                             {
  177.                                 DirEntry*                New;
  178.                                 FSSpec*                    Where;
  179.  
  180.                                 APRINT((" ReadDirectory filescan: '%p'",FileName));
  181.                                 /* allocate a new record to hold the directory */
  182.                                 New = (DirEntry*)AllocPtrCanFail(sizeof(DirEntry),"DirEntry");
  183.                                 if (New == NIL)
  184.                                     {
  185.                                         /* this operation is legal because the directory structure is */
  186.                                         /* always consistent at all times. */
  187.                                      FailurePoint:
  188.                                         DisposeDirectory(Dir);
  189.                                         APRINT(("-ReadDirectory failed"));
  190.                                         return NIL;
  191.                                     }
  192.                                 /* make a file specification that references the item */
  193.                                 Where = (FSSpec*)AllocPtrCanFail(sizeof(FSSpec),"DirFSSpec");
  194.                                 if (Where == NIL)
  195.                                     {
  196.                                      WhereFailurePoint:
  197.                                         ReleasePtr((char*)New);
  198.                                         goto FailurePoint;
  199.                                     }
  200.                                 EXECUTE(if (!Eep_RegisterFileSpec((FileSpec*)Where))
  201.                                     {ReleasePtr((char*)Where); goto WhereFailurePoint;})
  202.                                 Error = FSMakeFSSpec(VolumeRefNum,DirectoryID,FileName,Where);
  203.                                 if (Error != noErr)
  204.                                     {
  205.                                         APRINT((" ReadDirectory filescan:  Error %s making FSSpec",Error));
  206.                                         ReleasePtr((char*)New);
  207.                                         DisposeFileSpec((FileSpec*)Where);
  208.                                         goto TrySomeMore;
  209.                                     }
  210.                                 ERROR(((FSSpec*)Where)->name[0] == 0,PRERR(AllowResume,
  211.                                     "ReadDirectory:  FSSpec has empty filename"));
  212.                                 if ((CInfo.hFileInfo.ioFlAttrib & 16/*majiknumber!*/) != 0)
  213.                                     {
  214.                                         /* it's a directory */
  215.                                         APRINT((" ReadDirectory filescan: Directory"));
  216.                                         New->ItemType = eDirectory;
  217.                                     }
  218.                                  else
  219.                                     {
  220.                                         Boolean                    TargetWasFolder;
  221.                                         Boolean                    TargetWasAlias;
  222.                                         FSSpec                    WhereCopy;
  223.  
  224.                                         /* it's a file, but is it an alias? */
  225.                                         WhereCopy = *Where; /* we don't want to keep the changes! */
  226.                                         AUDITFILESPEC(WhereCopy);
  227.                                         Error = ResolveAliasFile(&WhereCopy,False/*stopafterfirst*/,
  228.                                             &TargetWasFolder,&TargetWasAlias);
  229.                                         APRINT((" ResolveAliasFile:  Err = %s, Folder = %b, Alias = %b",
  230.                                             (short)Error,(short)TargetWasFolder,(short)TargetWasAlias));
  231.                                         if (TargetWasAlias && (Error == noErr))
  232.                                             {
  233.                                                 APRINT((" ReadDirectory filescan: Symbolic Link"));
  234.                                                 New->ItemType = eSymbolicLink;
  235.                                             }
  236.                                         else
  237.                                             {
  238.                                                 APRINT((" ReadDirectory filescan: File"));
  239.                                                 New->ItemType = eFile;
  240.                                             }
  241.                                     }
  242.                                 New->WhereIsIt = (FileSpec*)Where;
  243.                                 New->Next = NIL;
  244.                                 if (Tail != NIL)
  245.                                     {
  246.                                         Tail->Next = New;
  247.                                     }
  248.                                  else
  249.                                     {
  250.                                         Dir->List = New;
  251.                                     }
  252.                                 Tail = New;
  253.                              TrySomeMore:
  254.                                 ;/* we jump here after weird "Desktop DB" stuff */
  255.                             }
  256.                         else /* Error != noErr */
  257.                             {
  258.                                 /* error means there are no more items in the directory */
  259.                                 EnergizerBunny = False;
  260.                             }
  261.                     }
  262.             }
  263.          else
  264.             {
  265.                 OSErr                        Error;
  266.                 short                        Index;
  267.                 Str255                    DiskName;
  268.                 MyBoolean                EnergizerBunny;
  269.                 HParamBlockRec    Params;
  270.  
  271.                 /* root directory.  We fake up a directory containing all of the */
  272.                 /* mounted volumes, kind of like under UNIX, so that it looks like */
  273.                 /* there is only one huge file system. */
  274.                 EnergizerBunny = True;
  275.                 Index = 0;
  276.                 while (EnergizerBunny)
  277.                     {
  278.                         Index += 1; /* index started at 0 up there, so we increment first */
  279.                         Params.volumeParam.ioNamePtr = DiskName;
  280.                         Params.volumeParam.ioVRefNum = 0;  /* says "use the index" */
  281.                         Params.volumeParam.ioVolIndex = Index;
  282.                         Error = PBHGetVInfo(&Params,False/*synchronous*/);
  283.                         if (Error == noErr)
  284.                             {
  285.                                 DirEntry*            New;
  286.                                 FSSpec*                Where;
  287.  
  288.                                 /* add this "directory" to the list */
  289.                                 APRINT((" ReadDirectory volscan: %p",DiskName));
  290.                                 New = (DirEntry*)AllocPtrCanFail(sizeof(DirEntry),"DirEntry");
  291.                                 if (New == NIL)
  292.                                     {
  293.                                         /* this operation is legal because the directory structure is */
  294.                                         /* always consistent at all times. */
  295.                                      DeathPoint:
  296.                                         DisposeDirectory(Dir);
  297.                                         APRINT(("-ReadDirectory failed"));
  298.                                         return NIL;
  299.                                     }
  300.                                 Where = (FSSpec*)AllocPtrCanFail(sizeof(FSSpec),"FSSpec");
  301.                                 if (Where == NIL)
  302.                                     {
  303.                                      AnotherWhereFailurePoint:
  304.                                         ReleasePtr((char*)New);
  305.                                         goto DeathPoint;
  306.                                     }
  307.                                 EXECUTE(if (!Eep_RegisterFileSpec((FileSpec*)Where))
  308.                                     {ReleasePtr((char*)Where); goto AnotherWhereFailurePoint;})
  309.                                 FSMakeFSSpec(Params.volumeParam.ioVRefNum,0/*don'tknowroot*/,
  310.                                     DiskName,Where);
  311.                                 New->WhereIsIt = (FileSpec*)Where;
  312.                                 New->ItemType = eDirectory;
  313.                                 New->Next = NIL;
  314.                                 if (Tail != NIL)
  315.                                     {
  316.                                         Tail->Next = New;
  317.                                     }
  318.                                  else
  319.                                     {
  320.                                         Dir->List = New;
  321.                                     }
  322.                                 Tail = New;
  323.                             }
  324.                          else
  325.                             {
  326.                                 /* error -- no more volumes mounted.  We is done */
  327.                                 EnergizerBunny = False;
  328.                             }
  329.                     }
  330.             }
  331.         return Dir;
  332.         APRINT(("-ReadDirectory %xl",Dir));
  333.     }
  334.  
  335.  
  336. /* get rid of the directory structure when we are done with it */
  337. void                            DisposeDirectory(DirectoryRec* Dir)
  338.     {
  339.         CheckPtrExistence(Dir);
  340.         while (Dir->List != NIL)
  341.             {
  342.                 DirEntry*            Temp;
  343.  
  344.                 Temp = Dir->List;
  345.                 Dir->List = Dir->List->Next;
  346.                 DisposeFileSpec(Temp->WhereIsIt);
  347.                 ReleasePtr((char*)Temp);
  348.             }
  349.         ReleasePtr((char*)Dir);
  350.     }
  351.  
  352.  
  353. /* find out how many entries there are in the directory structure */
  354. long                            GetDirectorySize(DirectoryRec* Dir)
  355.     {
  356.         DirEntry*                Scan;
  357.         long                        Count;
  358.  
  359.         CheckPtrExistence(Dir);
  360.         Count = 0;
  361.         Scan = Dir->List;
  362.         while (Scan != NIL)
  363.             {
  364.                 Count += 1;
  365.                 Scan = Scan->Next;
  366.             }
  367.         return Count;
  368.     }
  369.  
  370.  
  371. /* return the item type of an indexed directory entry (indices start from 0) */
  372. /* Indices start from 0 up to GetDirectorySize() - 1 */
  373. DirFileTypes            GetDirectoryEntryType(DirectoryRec* Dir, long Index)
  374.     {
  375.         DirEntry*                Scan;
  376.         long                        Count;
  377.  
  378.         CheckPtrExistence(Dir);
  379.         Count = 0;
  380.         Scan = Dir->List;
  381.         while (Scan != NIL)
  382.             {
  383.                 if (Count == Index)
  384.                     {
  385.                         return Scan->ItemType;
  386.                     }
  387.                 Count += 1;
  388.                 Scan = Scan->Next;
  389.             }
  390.         EXECUTE(PRERR(ForceAbort,"GetDirectoryEntryType:  Index is out of range"));
  391.     }
  392.  
  393.  
  394. /* return a handle containing the name of the specified directory entry or NIL */
  395. /* if allocation failed */
  396. char*                            GetDirectoryEntryName(DirectoryRec* Dir, long Index)
  397.     {
  398.         DirEntry*                Scan;
  399.         long                        Count;
  400.  
  401.         CheckPtrExistence(Dir);
  402.         Count = 0;
  403.         Scan = Dir->List;
  404.         while (Scan != NIL)
  405.             {
  406.                 if (Count == Index)
  407.                     {
  408.                         char*                        NamePtr;
  409.  
  410.                         NamePtr = ExtractFileName(Scan->WhereIsIt);
  411.                         if (NamePtr == NIL)
  412.                             {
  413.                                 return NIL;
  414.                             }
  415.                         return NamePtr;
  416.                     }
  417.                 Count += 1;
  418.                 Scan = Scan->Next;
  419.             }
  420.         EXECUTE(PRERR(ForceAbort,"GetDirectoryEntryName:  Index is out of range"));
  421.     }
  422.  
  423.  
  424. /* get a file spec describing a directory entry */
  425. /* this entry is a standard FileSpec, the same type as used in the Files module */
  426. /* and should be manipulated and disposed using routines from Files */
  427. FileSpec*                    GetDirectoryEntryFileSpec(DirectoryRec* Dir, long Index)
  428.     {
  429.         DirEntry*                Scan;
  430.         long                        Count;
  431.  
  432.         CheckPtrExistence(Dir);
  433.         Count = 0;
  434.         Scan = Dir->List;
  435.         while (Scan != NIL)
  436.             {
  437.                 if (Count == Index)
  438.                     {
  439.                         return DuplicateFileSpec(Scan->WhereIsIt);
  440.                     }
  441.                 Count += 1;
  442.                 Scan = Scan->Next;
  443.             }
  444.         EXECUTE(PRERR(ForceAbort,"GetDirectoryEntryFileSpec:  Index is out of range"));
  445.     }
  446.  
  447.  
  448. typedef enum {eLessThan EXECUTE(= -4521), eEqualTo, eGreaterThan} SortTypes;
  449.  
  450. /* local routine for comparing the directories */
  451. static SortTypes    NoCaseSort2(char* Left, char* Right, long MaxCount)
  452.     {
  453.         unsigned char        LeftTemp;
  454.         unsigned char        RightTemp;
  455.  
  456.      LoopPoint:
  457.         if (((*Left == 0) && (*Right == 0)) || (MaxCount == 0))
  458.             {
  459.                 return eEqualTo;
  460.             }
  461.         LeftTemp = *Left;
  462.         if ((LeftTemp >= 'A') && (LeftTemp <= 'Z'))
  463.             {
  464.                 LeftTemp = LeftTemp - 'A' + 'a';
  465.             }
  466.         RightTemp = *Right;
  467.         if ((RightTemp >= 'A') && (RightTemp <= 'Z'))
  468.             {
  469.                 RightTemp = RightTemp - 'A' + 'a';
  470.             }
  471.         if (LeftTemp < RightTemp)
  472.             {
  473.                 return eLessThan;
  474.             }
  475.         if (LeftTemp > RightTemp)
  476.             {
  477.                 return eGreaterThan;
  478.             }
  479.         Left += 1;
  480.         Right += 1;
  481.         MaxCount -= 1;
  482.         goto LoopPoint;
  483.     }
  484.  
  485. static SortTypes    NoCaseSort(char* Left, char* Right)
  486.     {
  487.         unsigned char        LeftTemp;
  488.         unsigned char        RightTemp;
  489.         long                        MaxCount;
  490.  
  491.         MaxCount = PtrSize(Left);
  492.         if (PtrSize(Right) < MaxCount)
  493.             {
  494.                 MaxCount = PtrSize(Right);
  495.             }
  496.      LoopPoint:
  497.         if (((*Left == 0) && (*Right == 0)) || (MaxCount == 0))
  498.             {
  499.                 return eEqualTo;
  500.             }
  501.         LeftTemp = *Left;
  502.         if ((LeftTemp >= 'A') && (LeftTemp <= 'Z'))
  503.             {
  504.                 LeftTemp = LeftTemp - 'A' + 'a';
  505.             }
  506.         RightTemp = *Right;
  507.         if ((RightTemp >= 'A') && (RightTemp <= 'Z'))
  508.             {
  509.                 RightTemp = RightTemp - 'A' + 'a';
  510.             }
  511.         if (LeftTemp < RightTemp)
  512.             {
  513.                 return eLessThan;
  514.             }
  515.         if (LeftTemp > RightTemp)
  516.             {
  517.                 return eGreaterThan;
  518.             }
  519.         Left += 1;
  520.         Right += 1;
  521.         MaxCount -= 1;
  522.         goto LoopPoint;
  523.     }
  524.  
  525.  
  526. /* resort the directory alphabetically.  Returns True if it succeeded or */
  527. /* False if it failed. */
  528. MyBoolean                    ResortDirectory(DirectoryRec* Dir)
  529.     {
  530.         DirEntry*                OldThangs;
  531.  
  532.         CheckPtrExistence(Dir);
  533.         OldThangs = Dir->List;
  534.         if (OldThangs == NIL)
  535.             {
  536.                 return True; /* hey, nothing to sort */
  537.             }
  538.         Dir->List = OldThangs; /* first item doesn't sort anyway, so putting it on */
  539.         OldThangs = OldThangs->Next; /* right away simplifies things later... */
  540.         Dir->List->Next = NIL;
  541.         /* ...since we can always assume Dir->List != NIL */
  542.         while (OldThangs != NIL) /* yup, a good olde n^2 insertion sort */
  543.             {
  544.                 DirEntry*                OurItem;
  545.                 DirEntry*                DirScan;
  546.                 DirEntry*                DirLag;
  547.                 char*                        ItemName;
  548.                 char*                        ScanName;
  549.  
  550.                 OurItem = OldThangs;
  551.                 OldThangs = OldThangs->Next; /* advance before we destroy .Next */
  552.                 ItemName = ExtractFileName(OurItem->WhereIsIt);
  553.                 if (ItemName == NIL)
  554.                     {
  555.                      FailurePoint1:
  556.                         return False;
  557.                     }
  558.                 ERROR(PtrSize(ItemName) == 0,PRERR(AllowResume,
  559.                     "ResortDirectory:  An item's FSSpec has an empty name"));
  560.                 DirLag = NIL;
  561.                 DirScan = Dir->List;
  562.                 while (DirScan != NIL)
  563.                     {
  564.                         SortTypes                    CompareResult;
  565.  
  566.                         ScanName = ExtractFileName(DirScan->WhereIsIt);
  567.                         if (ScanName == NIL)
  568.                             {
  569.                                 ReleasePtr(ItemName);
  570.                                 goto FailurePoint1;
  571.                             }
  572.                         ERROR(PtrSize(ScanName) == 0,PRERR(AllowResume,
  573.                             "ResortDirectory:  An item's FSSpec has an empty name"));
  574.                         CompareResult = NoCaseSort(ItemName,ScanName);
  575.                         ReleasePtr(ScanName);
  576.                         switch (CompareResult)
  577.                             {
  578.                                 case eLessThan:
  579.                                     /* our item comes first, insert */
  580.                                     OurItem->Next = DirScan;
  581.                                     if (DirLag != NIL)
  582.                                         {
  583.                                             DirLag->Next = OurItem;
  584.                                         }
  585.                                      else
  586.                                         {
  587.                                             Dir->List = OurItem;
  588.                                         }
  589.                                     goto ExitInnerLoopSortPoint;
  590.                                     break;
  591.                                 case eEqualTo:
  592.                                     /* we continue to next one so that this is a stable sort. */
  593.                                     break;
  594.                                 case eGreaterThan:
  595.                                     /* we continue to next one. */
  596.                                     break;
  597.                             }
  598.                         DirLag = DirScan;
  599.                         DirScan = DirScan->Next;
  600.                     }
  601.                 /* if we got all the way through, then tack it on the end */
  602.                 ERROR(DirLag == NIL,PRERR(ForceAbort,"ResortDirectory:  Internal error"));
  603.                 OurItem->Next = DirLag->Next; /* DirLag != NIL; see outer while comment */
  604.                 DirLag->Next = OurItem;
  605.              ExitInnerLoopSortPoint: /* exited from inner loop since we were able to insert */
  606.                 ReleasePtr(ItemName);
  607.             }
  608.     }
  609.  
  610.  
  611. /* this compares to file specifications and returns True if they refer to the */
  612. /* same file */
  613. MyBoolean                    CompareFileSpecs(FileSpec* First, FileSpec* Second)
  614.     {
  615.         CheckPtrExistence(First);
  616.         CheckPtrExistence(Second);
  617.         ValidateFileSpec(First);
  618.         ValidateFileSpec(Second);
  619.         if ((((FSSpec*)First)->vRefNum != ((FSSpec*)Second)->vRefNum)
  620.             || (((FSSpec*)First)->parID != ((FSSpec*)Second)->parID))
  621.             {
  622.                 return False;
  623.             }
  624.         if (((FSSpec*)First)->name[0] != ((FSSpec*)Second)->name[0])
  625.             {
  626.                 return False;
  627.             }
  628.         /* remember to use a case sensitive compare for UNIX! */
  629.         return (eEqualTo == NoCaseSort2((char*)&(((FSSpec*)First)->name[1]),
  630.             (char*)&(((FSSpec*)Second)->name[1]),((FSSpec*)First)->name[0]));
  631.     }
  632.  
  633.  
  634. /* dereference a symbolic link one level only */
  635. FileSpec*                    DereferenceSymbolicLink(FileSpec* Source)
  636.     {
  637.         FSSpec*                    New;
  638.         OSErr                        Error;
  639.         Boolean                    TargetWasFolder;
  640.         Boolean                    TargetWasAlias;
  641.  
  642.         APRINT(("+DereferenceSymbolicLink %r",Source));
  643.         CheckPtrExistence(Source);
  644.         ValidateFileSpec(Source);
  645.         New = (FSSpec*)AllocPtrCanFail(sizeof(FSSpec),"FSSpec");
  646.         *New = *(FSSpec*)Source;
  647.         if (New == NIL)
  648.             {
  649.                 APRINT(("-DereferenceSymbolicLink Failed"));
  650.                 return NIL;
  651.             }
  652.         AUDITFILESPEC(*New);
  653.         Error = ResolveAliasFile(New,False/*onelevel*/,&TargetWasFolder,&TargetWasAlias);
  654.         APRINT((" ResolveAliasFile:  Err = %s, Folder = %b, Alias = %b",(short)Error,
  655.             (short)TargetWasFolder,(short)TargetWasAlias));
  656.         if (Error == noErr)
  657.             {
  658.                 if (TargetWasAlias)
  659.                     {
  660.                         EXECUTE(if (!Eep_RegisterFileSpec((FileSpec*)New))
  661.                             {ReleasePtr((char*)New); New = NIL;})
  662.                         /* it was an alias */
  663.                         APRINT(("-DereferenceSymbolicLink %r",New));
  664.                         return (FileSpec*)New;
  665.                     }
  666.             }
  667.         /* wasn't an alias, so we return NIL */
  668.         ReleasePtr((char*)New); /* hasn't been registered yet */
  669.         APRINT(("-DereferenceSymbolicLink NIL"));
  670.         return NIL;
  671.     }
  672.  
  673.  
  674. /* get root file specification.  On UNIX, this would return "/"; on Macintosh, */
  675. /* it returns a bogus file descriptor */
  676. FileSpec*                    GetRootFileSpec(void)
  677.     {
  678.         FSSpec*                        Thang;
  679.  
  680.         Thang = (FSSpec*)AllocPtrCanFail(sizeof(FSSpec),"RootFileSpec");
  681.         if (Thang != NIL)
  682.             {
  683.                 Thang->vRefNum = 0;
  684.                 Thang->parID = 0;
  685.                 Thang->name[0] = 0;
  686.                 if (!Eep_RegisterFileSpec((FileSpec*)Thang))
  687.                     {
  688.                         ReleasePtr((char*)Thang);
  689.                         Thang = NIL;
  690.                     }
  691.             }
  692.         return (FileSpec*)Thang;
  693.     }
  694.  
  695.  
  696. /* get statistics for a file */
  697. MyBoolean                    GetFileStatistics(struct FileSpec* File, FileInfoRec* InfoOut)
  698.     {
  699.         FInfo                        FinderInfo;
  700.         CInfoPBRec            InfoPB;
  701.         OSErr                        Error;
  702.         DateTimeRec            When;
  703.  
  704.         CheckPtrExistence(File);
  705.         ValidateFileSpec(File);
  706.         FSpGetFInfo((FSSpec*)File,&FinderInfo);
  707.         InfoOut->CreatorCode = FinderInfo.fdCreator;
  708.         InfoOut->FileTypeCode = FinderInfo.fdType;
  709.         InfoPB.hFileInfo.ioVRefNum = ((FSSpec*)File)->vRefNum;
  710.         InfoPB.hFileInfo.ioNamePtr = ((FSSpec*)File)->name;
  711.         InfoPB.hFileInfo.ioDirID = ((FSSpec*)File)->parID;
  712.         InfoPB.hFileInfo.ioFDirIndex = 0;
  713.         Error = PBGetCatInfo(&InfoPB,False);
  714.         if (Error != noErr)
  715.             {
  716.                 return False;
  717.             }
  718.         Secs2Date(InfoPB.hFileInfo.ioFlCrDat,&When);
  719.         InfoOut->CreationDate.Year = When.year;
  720.         InfoOut->CreationDate.Month = When.month - 1;
  721.         InfoOut->CreationDate.Day = When.day - 1;
  722.         InfoOut->CreationDate.Hour = When.hour;
  723.         InfoOut->CreationDate.Minute = When.minute;
  724.         InfoOut->CreationDate.Second = When.second;
  725.         InfoOut->CreationDate.DayOfTheWeek = When.dayOfWeek - 1;
  726.         Secs2Date(InfoPB.hFileInfo.ioFlMdDat,&When);
  727.         InfoOut->LastModificationDate.Year = When.year;
  728.         InfoOut->LastModificationDate.Month = When.month - 1;
  729.         InfoOut->LastModificationDate.Day = When.day - 1;
  730.         InfoOut->LastModificationDate.Hour = When.hour;
  731.         InfoOut->LastModificationDate.Minute = When.minute;
  732.         InfoOut->LastModificationDate.Second = When.second;
  733.         InfoOut->LastModificationDate.DayOfTheWeek = When.dayOfWeek - 1;
  734.         return True;
  735.     }
  736.  
  737.  
  738. /* create a new directory with the specified file specification. */
  739. MyBoolean                    CreateNewDirectory(struct FileSpec* DirLocation)
  740.     {
  741.         long                        NewDirID;
  742.  
  743.         CheckPtrExistence(DirLocation);
  744.         ValidateFileSpec(DirLocation);
  745.         return (noErr == FSpDirCreate((FSSpec*)DirLocation,smSystemScript,&NewDirID));
  746.     }
  747.  
  748.  
  749. /* find out of the specified file specification is a directory */
  750. MyBoolean                    IsTheFileSpecADirectory(struct FileSpec* Spec)
  751.     {
  752.         OSErr                    Error;
  753.         Boolean                TargetWasFolder;
  754.         Boolean                TargetWasAlias;
  755.         FSSpec                Copy;
  756.  
  757.         APRINT(("+IsTheFileSpecADirectory %r",Spec));
  758.         CheckPtrExistence(Spec);
  759.         ValidateFileSpec(Spec);
  760.         Copy = *(FSSpec*)Spec;
  761.         AUDITFILESPEC(Copy);
  762.         Error = ResolveAliasFile(&Copy,True/*all of them*/,&TargetWasFolder,&TargetWasAlias);
  763.         APRINT((" ResolveAliasFile:  Err = %s, Folder = %b, Alias = %b",(short)Error,
  764.             (short)TargetWasFolder,(short)TargetWasAlias));
  765.         if (Error == fnfErr)
  766.             {
  767.                 /* assume it's a volume. */
  768.                 APRINT(("-IsTheFileSpecADirectory True (assumed volume)"));
  769.                 return True;
  770.             }
  771.         APRINT(("-IsTheFileSpecADirectory %b",(short)TargetWasFolder));
  772.         return TargetWasFolder;
  773.     }
  774.  
  775.  
  776. /* find out if the specified file is a symbolic link */
  777. MyBoolean                    IsTheFileSpecASymbolicLink(struct FileSpec* Spec)
  778.     {
  779.         OSErr                    Error;
  780.         Boolean                TargetWasFolder;
  781.         Boolean                TargetWasAlias;
  782.         FSSpec                Copy;
  783.  
  784.         APRINT(("+IsTheFileSpecASymbolicLink %r",Spec));
  785.         CheckPtrExistence(Spec);
  786.         ValidateFileSpec(Spec);
  787.         Copy = *(FSSpec*)Spec;
  788.         AUDITFILESPEC(Copy);
  789.         Error = ResolveAliasFile(&Copy,False/*one of them*/,&TargetWasFolder,&TargetWasAlias);
  790.         APRINT((" ResolveAliasFile:  Err = %s, Folder = %b, Alias = %b",(short)Error,
  791.             (short)TargetWasFolder,(short)TargetWasAlias));
  792.         if (Error == fnfErr)
  793.             {
  794.                 /* assume it's a volume. */
  795.                 APRINT(("-IsTheFileSpecASymbolicLink False (assumed volume)"));
  796.                 return False;
  797.             }
  798.         APRINT(("-IsTheFileSpecASymbolicLink %b",(short)TargetWasAlias));
  799.         return TargetWasAlias;
  800.     }
  801.  
  802.  
  803. /* obtain a file spec for a file inside of the specified directory.  the file */
  804. /* name must be a non-null-terminated heap block */
  805. struct FileSpec*    FileSpecForFileInDirectory(struct FileSpec* DirLocation,
  806.                                         char* Filename)
  807.     {
  808.         FSSpec*                    Result;
  809.         OSErr                        Error;
  810.         Boolean                    TargetWasFolder;
  811.         Boolean                    TargetWasAlias;
  812.         FSSpec                    Copy;
  813.         long                        DirID;
  814.         Str63                        Name;
  815.         long                        Scan;
  816.         long                        Limit;
  817.  
  818.         APRINT(("+FileSpecForFileInDirectory Dir=%r, Name=%r",DirLocation,Filename));
  819.  
  820.         CheckPtrExistence(DirLocation);
  821.         ValidateFileSpec(DirLocation);
  822.         CheckPtrExistence(Filename);
  823.  
  824.         Result = (FSSpec*)AllocPtrCanFail(sizeof(FSSpec),"FileSpecForFileInDirectory FSSpec");
  825.         if (Result == NIL)
  826.             {
  827.              FailurePoint1:
  828.                 APRINT(("-FileSpecForFileInDirectory failed"));
  829.                 return NIL;
  830.             }
  831.         Copy = *(FSSpec*)DirLocation;
  832.         AUDITFILESPEC(Copy);
  833.         Error = ResolveAliasFile(&Copy,True/*all of them*/,&TargetWasFolder,&TargetWasAlias);
  834.         APRINT((" ResolveAliasFile:  Err = %s, Folder = %b, Alias = %b",(short)Error,
  835.             (short)TargetWasFolder,(short)TargetWasAlias));
  836.         if (Error == fnfErr)
  837.             {
  838.                 /* assume it's a volume. */
  839.              FailurePoint2:
  840.                 ReleasePtr((char*)Result);
  841.                 goto FailurePoint1;
  842.             }
  843.         if (!TargetWasFolder)
  844.             {
  845.              FailurePoint3:
  846.                 goto FailurePoint2;
  847.             }
  848.         if (noErr != FDerefFolder(&Copy,&DirID))
  849.             {
  850.              FailurePoint4:
  851.                 goto FailurePoint3;
  852.             }
  853.         Limit = PtrSize(Filename);
  854.         if (Limit > 31)
  855.             {
  856.                 Limit = 31;
  857.             }
  858.         Name[0] = Limit;
  859.         for (Scan = 0; Scan < Limit; Scan += 1)
  860.             {
  861.                 Name[1 + Scan] = Filename[Scan];
  862.             }
  863.         Error = FSMakeFSSpec(((FSSpec*)DirLocation)->vRefNum,DirID,Name,Result);
  864.         APRINT((" ResolveAliasFile:  FSMakeFSSpec returned %s",(short)Error));
  865.         if ((noErr != Error) && (fnfErr != Error))
  866.             {
  867.                 /* file not found is natural, since they're probably creating it. */
  868.              FailurePoint5:
  869.                 goto FailurePoint4;
  870.             }
  871.         if (!Eep_RegisterFileSpec((FileSpec*)Result))
  872.             {
  873.              FailurePoint6:
  874.                 goto FailurePoint5;
  875.             }
  876.  
  877.         APRINT(("-FileSpecForFileInDirectory %r",Result));
  878.         return (FileSpec*)Result;
  879.     }
  880.